/*
 * Copyright 2009 ZXing authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.google.zxing.integration.android;

import android.app.AlertDialog;
import android.app.Activity;
import android.content.ActivityNotFoundException;
import android.content.DialogInterface;
import android.content.Intent;
import android.net.Uri;

/**
 * <p>
 * A utility class which helps ease integration with Barcode Scanner via
 * {@link Intent}s. This is a simple way to invoke barcode scanning and receive
 * the result, without any need to integrate, modify, or learn the project's
 * source code.
 * </p>
 * 
 * <h2>Initiating a barcode scan</h2>
 * 
 * <p>
 * Integration is essentially as easy as calling {@link #initiateScan(Activity)}
 * and waiting for the result in your app.
 * </p>
 * 
 * <p>
 * It does require that the Barcode Scanner application is installed. The
 * {@link #initiateScan(Activity)} method will prompt the user to download the
 * application, if needed.
 * </p>
 * 
 * <p>
 * There are a few steps to using this integration. First, your {@link Activity}
 * must implement the method {@link Activity#onActivityResult(int, int, Intent)}
 * and include a line of code like this:
 * </p>
 * 
 * <p>
 * {@code public void onActivityResult(int requestCode, int resultCode, Intent
 * intent) IntentResult scanResult =
 * IntentIntegrator.parseActivityResult(requestCode, resultCode, intent); if
 * (scanResult != null) { // handle scan result } // else continue with any
 * other code you need in the method ... } }
 * </p>
 * 
 * <p>
 * This is where you will handle a scan result. Second, just call this in
 * response to a user action somewhere to begin the scan process:
 * </p>
 * 
 * <p>
 * {@code IntentIntegrator.initiateScan(yourActivity);}
 * </p>
 * 
 * <p>
 * You can use
 * {@link #initiateScan(Activity, CharSequence, CharSequence, CharSequence, CharSequence)}
 * or {@link #initiateScan(Activity, int, int, int, int)} to customize the
 * download prompt with different text labels.
 * </p>
 * 
 * <p>
 * Note that {@link #initiateScan(Activity)} returns an {@link AlertDialog}
 * which is non-null if the user was prompted to download the application. This
 * lets the calling app potentially manage the dialog. In particular, ideally,
 * the app dismisses the dialog if it's still active in its
 * {@link Activity#onPause()} method.
 * </p>
 * 
 * <h2>Sharing text via barcode</h2>
 * 
 * <p>
 * To share text, encoded as a QR Code on-screen, similarly, see
 * {@link #shareText(Activity, CharSequence)}.
 * </p>
 * 
 * <p>
 * Some code, particularly download integration, was contributed from the
 * Anobiit application.
 * </p>
 * 
 * @author Sean Owen
 * @author Fred Lin
 * @author Isaac Potoczny-Jones
 * @author Brad Drehmer
 */
public final class IntentIntegrator {

	public static final int REQUEST_CODE = 0x0ba7c0de; // get it?

	public static final String DEFAULT_TITLE = "Install Barcode Scanner?";
	public static final String DEFAULT_MESSAGE = "This application requires Barcode Scanner. Would you like to install it?";
	public static final String DEFAULT_YES = "Yes";
	public static final String DEFAULT_NO = "No";

	// supported barcode formats
	public static final String PRODUCT_CODE_TYPES = "UPC_A,UPC_E,EAN_8,EAN_13";
	public static final String ONE_D_CODE_TYPES = PRODUCT_CODE_TYPES
			+ ",CODE_39,CODE_93,CODE_128";
	public static final String QR_CODE_TYPES = "QR_CODE";
	public static final String ALL_CODE_TYPES = null;

	private IntentIntegrator() {
	}

	/**
	 * See
	 * {@link #initiateScan(Activity, CharSequence, CharSequence, CharSequence, CharSequence)}
	 * -- same, but uses default English labels.
	 */
	public static AlertDialog initiateScan(Activity activity) {
		return initiateScan(activity, DEFAULT_TITLE, DEFAULT_MESSAGE,
				DEFAULT_YES, DEFAULT_NO);
	}

	/**
	 * See
	 * {@link #initiateScan(Activity, CharSequence, CharSequence, CharSequence, CharSequence)}
	 * -- same, but takes string IDs which refer to the {@link Activity}'s
	 * resource bundle entries.
	 */
	public static AlertDialog initiateScan(Activity activity, int stringTitle,
			int stringMessage, int stringButtonYes, int stringButtonNo) {
		return initiateScan(activity, activity.getString(stringTitle),
				activity.getString(stringMessage),
				activity.getString(stringButtonYes),
				activity.getString(stringButtonNo));
	}

	/**
	 * See
	 * {@link #initiateScan(Activity, CharSequence, CharSequence, CharSequence, CharSequence, CharSequence)}
	 * -- same, but scans for all supported barcode types.
	 * 
	 * @param stringTitle
	 *            title of dialog prompting user to download Barcode Scanner
	 * @param stringMessage
	 *            text of dialog prompting user to download Barcode Scanner
	 * @param stringButtonYes
	 *            text of button user clicks when agreeing to download Barcode
	 *            Scanner (e.g. "Yes")
	 * @param stringButtonNo
	 *            text of button user clicks when declining to download Barcode
	 *            Scanner (e.g. "No")
	 * @return an {@link AlertDialog} if the user was prompted to download the
	 *         app, null otherwise
	 */
	public static AlertDialog initiateScan(Activity activity,
			CharSequence stringTitle, CharSequence stringMessage,
			CharSequence stringButtonYes, CharSequence stringButtonNo) {

		return initiateScan(activity, stringTitle, stringMessage,
				stringButtonYes, stringButtonNo, ALL_CODE_TYPES);
	}

	/**
	 * Invokes scanning.
	 * 
	 * @param stringTitle
	 *            title of dialog prompting user to download Barcode Scanner
	 * @param stringMessage
	 *            text of dialog prompting user to download Barcode Scanner
	 * @param stringButtonYes
	 *            text of button user clicks when agreeing to download Barcode
	 *            Scanner (e.g. "Yes")
	 * @param stringButtonNo
	 *            text of button user clicks when declining to download Barcode
	 *            Scanner (e.g. "No")
	 * @param stringDesiredBarcodeFormats
	 *            a comma separated list of codes you would like to scan for.
	 * @return an {@link AlertDialog} if the user was prompted to download the
	 *         app, null otherwise
	 * @throws InterruptedException
	 *             if timeout expires before a scan completes
	 */
	public static AlertDialog initiateScan(Activity activity,
			CharSequence stringTitle, CharSequence stringMessage,
			CharSequence stringButtonYes, CharSequence stringButtonNo,
			CharSequence stringDesiredBarcodeFormats) {
		Intent intentScan = new Intent("com.google.zxing.client.android.SCAN");
		intentScan.addCategory(Intent.CATEGORY_DEFAULT);

		// check which types of codes to scan for
		if (stringDesiredBarcodeFormats != null) {
			// set the desired barcode types
			intentScan.putExtra("SCAN_FORMATS", stringDesiredBarcodeFormats);
		}

		try {
			activity.startActivityForResult(intentScan, REQUEST_CODE);
			return null;
		} catch (ActivityNotFoundException e) {
			return showDownloadDialog(activity, stringTitle, stringMessage,
					stringButtonYes, stringButtonNo);
		}
	}

	private static AlertDialog showDownloadDialog(final Activity activity,
			CharSequence stringTitle, CharSequence stringMessage,
			CharSequence stringButtonYes, CharSequence stringButtonNo) {
		AlertDialog.Builder downloadDialog = new AlertDialog.Builder(activity);
		downloadDialog.setTitle(stringTitle);
		downloadDialog.setMessage(stringMessage);
		downloadDialog.setPositiveButton(stringButtonYes,
				new DialogInterface.OnClickListener() {
					public void onClick(DialogInterface dialogInterface, int i) {
						Uri uri = Uri
								.parse("market://search?q=pname:com.google.zxing.client.android");
						Intent intent = new Intent(Intent.ACTION_VIEW, uri);
						activity.startActivity(intent);
					}
				});
		downloadDialog.setNegativeButton(stringButtonNo,
				new DialogInterface.OnClickListener() {
					public void onClick(DialogInterface dialogInterface, int i) {
					}
				});
		return downloadDialog.show();
	}

	/**
	 * <p>
	 * Call this from your {@link Activity}'s
	 * {@link Activity#onActivityResult(int, int, Intent)} method.
	 * </p>
	 * 
	 * @return null if the event handled here was not related to
	 *         {@link IntentIntegrator}, or else an {@link IntentResult}
	 *         containing the result of the scan. If the user cancelled
	 *         scanning, the fields will be null.
	 */
	public static IntentResult parseActivityResult(int requestCode,
			int resultCode, Intent intent) {
		if (requestCode == REQUEST_CODE) {
			if (resultCode == Activity.RESULT_OK) {
				String contents = intent.getStringExtra("SCAN_RESULT");
				String formatName = intent.getStringExtra("SCAN_RESULT_FORMAT");
				return new IntentResult(contents, formatName);
			} else {
				return new IntentResult(null, null);
			}
		}
		return null;
	}

	/**
	 * See
	 * {@link #shareText(Activity, CharSequence, CharSequence, CharSequence, CharSequence, CharSequence)}
	 * -- same, but uses default English labels.
	 */
	public static void shareText(Activity activity, CharSequence text) {
		shareText(activity, text, DEFAULT_TITLE, DEFAULT_MESSAGE, DEFAULT_YES,
				DEFAULT_NO);
	}

	/**
	 * See
	 * {@link #shareText(Activity, CharSequence, CharSequence, CharSequence, CharSequence, CharSequence)}
	 * -- same, but takes string IDs which refer to the {@link Activity}'s
	 * resource bundle entries.
	 */
	public static void shareText(Activity activity, CharSequence text,
			int stringTitle, int stringMessage, int stringButtonYes,
			int stringButtonNo) {
		shareText(activity, text, activity.getString(stringTitle),
				activity.getString(stringMessage),
				activity.getString(stringButtonYes),
				activity.getString(stringButtonNo));
	}

	/**
	 * Shares the given text by encoding it as a barcode, such that another user
	 * can scan the text off the screen of the device.
	 * 
	 * @param text
	 *            the text string to encode as a barcode
	 * @param stringTitle
	 *            title of dialog prompting user to download Barcode Scanner
	 * @param stringMessage
	 *            text of dialog prompting user to download Barcode Scanner
	 * @param stringButtonYes
	 *            text of button user clicks when agreeing to download Barcode
	 *            Scanner (e.g. "Yes")
	 * @param stringButtonNo
	 *            text of button user clicks when declining to download Barcode
	 *            Scanner (e.g. "No")
	 */
	public static void shareText(Activity activity, CharSequence text,
			CharSequence stringTitle, CharSequence stringMessage,
			CharSequence stringButtonYes, CharSequence stringButtonNo) {

		Intent intent = new Intent();
		intent.setAction("com.google.zxing.client.android.ENCODE");
		intent.putExtra("ENCODE_TYPE", "TEXT_TYPE");
		intent.putExtra("ENCODE_DATA", text);
		try {
			activity.startActivity(intent);
		} catch (ActivityNotFoundException e) {
			showDownloadDialog(activity, stringTitle, stringMessage,
					stringButtonYes, stringButtonNo);
		}
	}

}
